
package artistic;

import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.util.Arrays;

/**
 * Class for storing a 64x64 pixel
 * tile of image data.
 *
 * @author Mikael Andersson
 * @author Rurik Högfeldt
 */

public class CanvasTile {
    //Standard fill types
    public enum FillType
    {
        TRANSPARENT,
        BLACK,
        WHITE,
        CHECKERBOARD
    }

    //Create a 64*64 entries large array
    int[] pixelArray = new int[1<<12];

    public CanvasTile()
    {
        this(FillType.WHITE);
    }

    public CanvasTile(FillType fillType)
    {
        fill(fillType);
    }

    public void fill(FillType fillType)
    {
        switch(fillType)
        {
            case BLACK:
                Arrays.fill(pixelArray, 0xff000000);
                break;
            case WHITE:
                Arrays.fill(pixelArray, 0xffffffff);
                break;
            case TRANSPARENT:
                Arrays.fill(pixelArray, 0x00777777);
                break;
            case CHECKERBOARD:
                //Fill with checkerboard pattern
                for(int i=0; i<(1<<12); ++i)
                {
                    pixelArray[i] =
                            ((i^(i>>6))&8)==0?
                                0xff999999: //light color
                                0xff666666; //dark color
                }
                break;
            default:
                throw new RuntimeException("Fill type " +
                        fillType.toString() + "is not implemented");
        }
        changed = true;
    }

    /**
     * Get array representation of image data
     * in tile, as array of signed 32-bit ints.
     *
     * @return Internal image data.
     */
    public int[] getRgbaArray()
    {
        return pixelArray;
    }

    public int getPixel(int x, int y)
    {
        try {
            return pixelArray[(y<<6)+x];
        }
        catch(ArrayIndexOutOfBoundsException e)
        {
            System.err.println("Warning: Attempted pixel read outside of tile bounds.");
            return 0xffffffff;
        }
    }

    /**
     * Converts an array of the form {r, g, b, a}
     * to a packed int of the form 0xAARRGGBB
     *
     * @param packed
     * @return
     */
    public static int toPackedRGBA(int[] array)
    {
        assert(array.length == 4);
        return (array[0] << 16) | (array[1] << 8) |
                array[2] | (array[3] << 24);
    }

    /**
     * Converts a packed int of the form 0xAARRGGBB
     * to an array of the form {r, g, b, a}
     *
     * @param packed
     * @return
     */
    public static int[] fromPackedRGBA(int packed)
    {
        int[] array = new int[4];
        
        array[0] = ((packed >>> 16) & 0xff);
        array[1] = ((packed >>> 8) & 0xff);
        array[2] = ((packed >>> 0) & 0xff);
        array[3] = ((packed >>> 24) & 0xff);

        return array;
    }

    public void drawPixel(int x, int y, int color)
    {
        drawPixel(x, y, color, 255);
    }

    public void drawPixel(int x, int y, int color, int opacity)
    {
        if(opacity == 0)
            return;

        int alpha = ((color>>>24)*opacity)>>>8;

        //Just set pixel for non-transparent colors
        if(alpha == 255)
        {
            setPixel(x,y,color);
            return;
        }
        
        int[] oldColor = fromPackedRGBA(getPixel(x, y));
        int[] newColor = fromPackedRGBA(color);

        for(int channel = 0; channel<3; ++channel)
        {
            int newCh = newColor[channel];
            newColor[channel] = newCh;
        }
        
        newColor[3] = Math.min(alpha+oldColor[3], 255);

        setPixel(x,y,toPackedRGBA(newColor));
    }

    public void setPixel(int x, int y, int color)
    {
        try {
            pixelArray[(y<<6)+x] = color;
            changed = true;
        }
        catch(ArrayIndexOutOfBoundsException e)
        {
            System.err.println("Warning: pixel drawn outside of tile bounds.");
        }
    }

    public void draw(Graphics g, int x, int y)
    {
        BufferedImage image =
                new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB);

        int[] array = pixelArray.clone();
        for(int i=0; i<array.length; ++i)
        {
            array[i] |= 0xff000000;
        }
        image.setRGB(0, 0, 64, 64, array, 0, 64);
        g.drawImage(image, x, y, null);
    }

    public void copyFrom(CanvasTile tile)
    {
        int[] source = tile.pixelArray;
        for(int i = 0; i<(1<<12); ++i)
        {
            pixelArray[i] = source[i];
        }
        changed = true;
    }

    private boolean changed = true;

    public void setChanged(boolean state)
    {
        changed = state;
    }

    public boolean isChanged()
    {
        return changed;
    }
}
